
package traffic;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.*;

public class SwingGUI extends javax.swing.JFrame
{
    final static String USERCARN = "UserCarsN.png";
    final static String USERCARE = "UserCarsE.png";
    final static String USERCARS = "UserCarsS.png";
    final static String USERCARW = "UserCarsW.png";
    final static String BACKGROUND = "Bg-Design.jpg";
    final static String STREET = "STREET-BG.jpg";
    final static String NEN = "NEN.png";
    final static String NEE = "NEE.png";
    final static String NWN = "NWN.png";
    final static String NWW = "NWW.png";
    final static String SEE = "SEE.png";
    final static String SES = "SES.png";
    final static String SWS = "SWS.png";
    final static String SWW = "SWW.png";
    final static String CARN = "carN.png";
    final static String CARE = "carE.png";
    final static String CARS = "carS.png";
    final static String CARW = "carW.png";
    
    Thread lightThread, carThread; //3 threads total, main thread (will handle buttons), light thread, and car thread
    ImageIcon images;
    JLabel bg, streets, LSSliderLabel, carSpeedSliderLabel, carNumberLabel;
    JLayeredPane bgLayer, lightsLayer, carLayer, buttonLayer; //all buttons go onto the bgLayer at layer level "1" (bg image is at layer level 0)
    JLabel[][] lightImageMatrix = new JLabel[10][10]; //array of light images, used to display our lights
    ArrayList carImageArray = new ArrayList(); //allowing for up to 100 cars, we CAN increase this, but only after up to 100 are spawned initially, ask me if you want to know more
    JLabel carImageBuffer, userCarLabel1, userCarLabel2, lightSchemeLabel;
    JSlider lightSpeed; //this slider will control the amount of time between switching light states
    JSlider carSpeed, carNumber;
    JTextField userCarStart, userCarDest;
    JTextArea userCarFeedback;
    JButton userCarButton, lightScheme1, lightScheme2;
    LightsControl lc; //This handles the lights
    CarsControl car = new CarsControl(); //handles the cars
    
    
	

    public SwingGUI() //calls to initialize components, and creates threads for the lights and cars
    {
        super("Traffic");
        initializeComponents(); //gets everything set up in the beginning
        
        
        Runnable lightExecution = new Runnable() //defines what the light thread will do
        {
            public void run() 
            {
                try 
                {
                    lightsAnimator(); //lights animator will have everything necessary for operating the lights
                    Thread.sleep(0); //this segment is never actually reached, and necessary for the try/catch, not sure if we actually need the try/catch, just part of the example I saw
                }
                catch (InterruptedException ie) 
                {
                    
                }
            } 
            
        };
        lightThread = new Thread(lightExecution); //creating thread for lights since we need concurrent execution of the cars class
        lightThread.start();
        
        Runnable carExecution = new Runnable() //defines what the light thread will do
        {
            public void run() 
            {
                try 
                {
                    carAnimator();
                    Thread.sleep(0);
                }
                catch (InterruptedException ie) 
                {
                    
                }
            } 
            
        };
        carThread = new Thread(carExecution); //creating thread for lights since we need concurrent execution of the cars class
        carThread.start();
        
    }
    
    public class lightSpeedCL implements ChangeListener //this is a listener for the light speed slider, and changes the value of the light speed and JLabel accordingly
    {
        public void stateChanged(ChangeEvent ce)
        {
            int value = lightSpeed.getValue();
            double toSeconds = 0.001;
            
            String str = Double.toString(value*toSeconds);
            LSSliderLabel.setText(str+"s");
        }
    }
    
    public class carSpeedCL implements ChangeListener //this is a listener for the car speed slider, and changes the value of the car speed and JLabel accordingly
    {
        public void stateChanged(ChangeEvent ce)
        {
            int value = carSpeed.getValue();
            
            String str = Integer.toString(value);
            carSpeedSliderLabel.setText(str+"ms");
        }
    }
    
    public class carNumberCL implements ChangeListener //this is a listener for the car number slider, and changes the value of the car number and JLabel accordingly
    {
        public void stateChanged(ChangeEvent ce)
        {
            int value = carNumber.getValue();
            
            String str = Integer.toString(value);
            carNumberLabel.setText(str);
        }
    }
    
    public class userCarButtonAL implements ActionListener //button to activate the user car with given beginning and end, with error checking
    {
        public void actionPerformed(ActionEvent evt) 
        {
            //create new car using input from the two text fields
            userCarButton.setEnabled(false);
            int start, end, dir, numCars;
            String startBuffer, endBuffer;
            char character;
            boolean isGood = true;
            
            startBuffer = userCarStart.getText();
            endBuffer = userCarDest.getText();
            
            for(int i = 0; i < startBuffer.length(); i++)
            {
                character = startBuffer.charAt(i);
                if(character > 57 || character < 48) //if all characters are numeric
                {
                    isGood = false;
                    userCarButton.setEnabled(true);
                    userCarFeedback.setText("One of the characters in the text fields is not a numeral.");
                }
            }
            
            for(int i = 0; i < endBuffer.length(); i++)
            {
                character = endBuffer.charAt(i);
                if(character > 57 || character < 48) //if all characters are numberic
                {
                    isGood = false;
                    userCarButton.setEnabled(true);
                    userCarFeedback.setText("One of the characters in the text fields is not a numeral.");
                }
            }
            
            if(startBuffer.length() == 0 || endBuffer.length() == 0)
            {
                isGood = false;
                userCarButton.setEnabled(true);
                userCarFeedback.setText("The text field is empty.");
            }
            
            if(isGood)
            {
                start = Integer.parseInt(startBuffer);
                end = Integer.parseInt(endBuffer);
                
                if(start > 99 || end > 99)
                {
                    userCarButton.setEnabled(true);
                    userCarFeedback.setText("The numbers are too large!");
                }
                else if(car.getOMValue(start/10, start%10) == 1)
                {
                    userCarButton.setEnabled(true);
                    userCarFeedback.setText("That starting intersection is currently occupied.");
                }
                else if(start == end)
                {
                    userCarButton.setEnabled(true);
                    userCarFeedback.setText("The start and end locations are the same.");
                }
                else
                {
                    userCarFeedback.setText("Car creation successful!");
                    carImageBuffer = new JLabel();
                    
                    carImageBuffer.setBounds(-20, -20, 14, 14); //-20, -20 gets it just out of frame
                    carLayer.add(carImageBuffer, new Integer(0)); //adding cars to the car layer at level 0, but the car layer itself will be level 2 in the bgLayer
                    carImageArray.add(carImageBuffer);
                    car.spawnUserCar(start, end);
                    numCars = car.getNumcars();
                    carImageBuffer = (JLabel)carImageArray.get(numCars-1);
                    carImageBuffer.setLocation(14*car.getCarFromArray(numCars-1).getOMJ(), 14*car.getCarFromArray(numCars-1).getOMI());
                    car.getCarFromArray(numCars-1).setPixelLoc(14*car.getCarFromArray(numCars-1).getOMJ(), 14*car.getCarFromArray(numCars-1).getOMI());
                    car.getCarFromArray(numCars-1).setAsUserCar();
                    dir = car.getCarFromArray(numCars-1).getDirection();
                    if(dir == 0)
                            carImageBuffer.setIcon(new ImageIcon(USERCARN));
                        else if(dir == 1)
                            carImageBuffer.setIcon(new ImageIcon(USERCARE));
                        else if(dir == 2)
                            carImageBuffer.setIcon(new ImageIcon(USERCARS));
                        else if(dir == 3)
                            carImageBuffer.setIcon(new ImageIcon(USERCARW));
                }
                
            }
        }
    }
    
    public class lightScheme1AL implements ActionListener //listener for the first light scheme button
    {
        public void actionPerformed(ActionEvent evt)
        {
            lc.changeLightScheme1();
            refreshLights();
        }
    }
    
    public class lightScheme2AL implements ActionListener //listener for the second light scheme button
    {
        public void actionPerformed(ActionEvent evt)
        {
            lc.changeLightScheme2();
            refreshLights();
        }
    }
    
    private void initializeComponents() //initializes the entire layout, including the background, lights, cars, and buttons, as well as program boundries, etc.
    {
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); //what to do when closing the window
        
        initBG();
        initLights();
        initCars();
        initButtons();
        
        GroupLayout layout = new GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        
        layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(bgLayer, GroupLayout.DEFAULT_SIZE, 800, Short.MAX_VALUE));
        layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING).addComponent(bgLayer, GroupLayout.Alignment.TRAILING, GroupLayout.DEFAULT_SIZE, 600, Short.MAX_VALUE));
        
        
        this.setResizable(false); //no resizing
        this.setBounds(100, 100, 800, 600); //starting x and y position, the other 2 numbers cannot override the previous setting
        this.setVisible(true); //definitely need to be able to see the window
        
        pack(); //not sure what this does exactly, the program works without it, but it was used in all of the examples I've seen, should probably look it up later
    }

    private void initBG() //initialize the background JlayeredPane, and ImageIcons to display the background and streets
    {
        bgLayer = new JLayeredPane(); //bg layer level 0, lights level 1, car level 2
        bg = new JLabel();
        streets = new JLabel();
        
        bg.setIcon(new ImageIcon(BACKGROUND));
        bg.setBounds(0, 0, 800, 600);
        bgLayer.add(bg, new Integer(0)); //the higher the number, the higher the layer
        
        streets.setIcon(new ImageIcon(STREET));
        streets.setBounds(241, 41, 518, 518);
        bgLayer.add(streets, new Integer(1));
    }
    
    private void initLights() //initialize the lights JlayeredPane, and ImageIcons to display the lights, as well as the slider for the lights
    {
        lightsLayer = new JLayeredPane();//each JLayeredPane has a level in the JFrame, and each component of a JLayered pane has levels within the layer (for sliders and JLabels etc.)
        LSSliderLabel = new JLabel("3.0s");
        lightSpeed = new JSlider(500, 5000, 3000); //(min value, max value, start value) time in ms for light thread sleep
        int i, j;
        
        for(i = 0; i < 10; i++) //initializing the light images, determining light directions based on i/j position
        {
            for(j = 0; j < 10; j++) //first 2 letters are the directions of the lights, the 3rd letter is the direction of the green light
            {
                lightImageMatrix[i][j] = new JLabel(); //create a jlabel for each light
                
                if(i%2 == 0 && j%2 == 0)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(NEN)); //create if's to determine which light image and pixel location
                else if(i%2 == 1 && j%2 == 0)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(NWN));
                else if(i%2 == 0 && j%2 == 1)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(SES));
                else if(i%2 == 1 && j%2 == 1)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(SWS));
                else
                    System.err.println("Error initializing light images.");
                
                lightImageMatrix[i][j].setBounds((56*j)-14, (56*i)-14, 42, 42); //each street is 14 pixels wide, blocks are 42 pixels wide
                
                lightsLayer.add(lightImageMatrix[i][j], new Integer(0)); //add the lights to the lights layer for display
            }
        }
        
        lightsLayer.setBounds(241, 41, 518, 518);
        bgLayer.add(lightsLayer, new Integer(2));
    }
    
    private void initCars() //initialize the cars JlayeredPane, and ImageIcons to display the cars
    {
        carLayer = new JLayeredPane();
        int i, numCars;
        numCars = car.getNumcars();
        
        for(i = 0; i < numCars; i++) //adding 100 car images off screen, when a car is "spawned" just move one on screen etc.
        {
            carImageBuffer = new JLabel();
            carImageBuffer.setIcon(new ImageIcon(CARS)); //these images are being spawned "out of frame" and simply moved to the correct place when the cars are spawned
            carImageBuffer.setBounds(-20, -20, 14, 14); //-20, -20 gets it just out of frame
            carLayer.add(carImageBuffer, new Integer(0)); //adding cars to the car layer at level 0, but the car layer itself will be level 2 in the bgLayer
            carImageArray.add(carImageBuffer);
        }
        
        carLayer.setBounds(241, 41, 518, 518); //sets bounds of car and light layer to only be within that square
        bgLayer.add(carLayer, new Integer(3));
    }
    
    private void initButtons() //initialize the buttons JlayeredPane, and JButtons / JLabels to show the buttons, as well as the slider for the cars
    {
        buttonLayer = new JLayeredPane();
        carSpeedSliderLabel = new JLabel("30ms");
        carNumberLabel = new JLabel("20");
        carSpeed = new JSlider(0, 100, 30);
        carNumber = new JSlider(1, 80, 20);
        userCarStart = new JTextField("0", 2);
        userCarDest = new JTextField("0", 2);
        userCarLabel1 = new JLabel("Create your own car!");
        userCarLabel2 = new JLabel("Enter any numbers 0 - 99:");
        userCarFeedback = new JTextArea(20, 2);
        userCarButton = new JButton("Go!");
        lightScheme1 = new JButton("1");
        lightScheme2 = new JButton("2");
        lightSchemeLabel = new JLabel("Lighting Schemes:");
        
        lightSchemeLabel.setBounds(10, 370, 200, 20);
        buttonLayer.add(lightSchemeLabel, new Integer(0));
        
        lightScheme1.setBounds(10, 390, 60, 30);
        lightScheme1.addActionListener(new lightScheme1AL());
        buttonLayer.add(lightScheme1, new Integer(0));
        
        lightScheme2.setBounds(80, 390, 60, 30);
        lightScheme2.addActionListener(new lightScheme2AL());
        buttonLayer.add(lightScheme2, new Integer(0));
        
        userCarStart.setBounds(10, 240, 70, 40);
        userCarStart.setBorder(BorderFactory.createTitledBorder("Start:"));
        userCarStart.setOpaque(false);
        buttonLayer.add(userCarStart, new Integer(0));
        
        userCarDest.setBounds(90, 240, 70, 40);
        userCarDest.setBorder(BorderFactory.createTitledBorder("End:"));
        userCarDest.setOpaque(false);
        buttonLayer.add(userCarDest, new Integer(0));
        
        userCarLabel1.setBounds(10, 210, 200, 13);
        buttonLayer.add(userCarLabel1, new Integer(0));
        userCarLabel2.setBounds(10, 225, 200, 13);
        buttonLayer.add(userCarLabel2, new Integer(0));
        
        userCarButton.setBounds(170, 248, 60, 30);
        userCarButton.addActionListener(new userCarButtonAL());
        buttonLayer.add(userCarButton, new Integer(0));
        
        userCarFeedback.setBounds(10, 290, 220, 80);
        userCarFeedback.setBorder(BorderFactory.createTitledBorder("Feedback:"));
        userCarFeedback.setOpaque(false);
        userCarFeedback.setEditable(false);
        userCarFeedback.setLineWrap(true);
        userCarFeedback.setWrapStyleWord(true);
        buttonLayer.add(userCarFeedback, new Integer(0));
        
        lightSpeed.setBorder(BorderFactory.createTitledBorder("Light Interval"));
        lightSpeed.setBounds(0, 20, 200, 60); //setBounds is formatted (x, y, width, height)
        lightSpeed.addChangeListener(new lightSpeedCL());
        lightSpeed.setMajorTickSpacing(500);
        lightSpeed.setPaintTicks(true);
        lightSpeed.setSnapToTicks(true);
        lightSpeed.setOpaque(false);
        buttonLayer.add(lightSpeed, new Integer(0));
        LSSliderLabel.setBounds(200, 40, 40, 20);
        buttonLayer.add(LSSliderLabel, new Integer(0));
        
        carSpeed.setBorder(BorderFactory.createTitledBorder("Car Speed (time per pixel)"));
        carSpeed.setBounds(0, 80, 200, 60);
        carSpeed.addChangeListener(new carSpeedCL());
        carSpeed.setMajorTickSpacing(10);
        carSpeed.setPaintTicks(true);
        carSpeed.setSnapToTicks(true);
        carSpeed.setOpaque(false);
        buttonLayer.add(carSpeed, new Integer(0));
        carSpeedSliderLabel.setBounds(200, 100, 40, 20);
        buttonLayer.add(carSpeedSliderLabel, new Integer(0));
        
        carNumber.setBorder(BorderFactory.createTitledBorder("Number of cars"));
        carNumber.setBounds(0, 140, 200, 60);
        carNumber.addChangeListener(new carNumberCL());
        carNumber.setOpaque(false);
        buttonLayer.add(carNumber, new Integer(0));
        carNumberLabel.setBounds(200, 160, 40, 20);
        buttonLayer.add(carNumberLabel, new Integer(0));
        
        //buttonLayer.setBorder(BorderFactory.createTitledBorder("Settings:"));
        //buttonLayer.setBorder(BorderFactory.createLineBorder(Color.RED));
        //buttonLayer.setBackground(Color.BLUE);
        //buttonLayer.setOpaque(true);
        buttonLayer.setBounds(0, 0, 241, 600);
        bgLayer.add(buttonLayer, new Integer(1));
    }
    
    private void refreshLights() //updates the image of each light depending on its current light state (greenstate variable)
    {
        int i, j;
        for(i = 0; i<10; i++)
        {
            for(j = 0; j<10; j++)
            {
                if(i%2 == 0 && j%2 == 0 && lc.getLightFromMatrix(i, j).getLightState() == 0)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(NEN));
                else if(i%2 == 0 && j%2 == 0 && lc.getLightFromMatrix(i, j).getLightState() == 1)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(NEE));
                else if(i%2 == 1 && j%2 == 0 && lc.getLightFromMatrix(i, j).getLightState() == 0)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(NWN));
                else if(i%2 == 1 && j%2 == 0 && lc.getLightFromMatrix(i, j).getLightState() == 1)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(NWW));
                else if(i%2 == 0 && j%2 == 1 && lc.getLightFromMatrix(i, j).getLightState() == 0)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(SES));
                else if(i%2 == 0 && j%2 == 1 && lc.getLightFromMatrix(i, j).getLightState() == 1)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(SEE));
                else if(i%2 == 1 && j%2 == 1 && lc.getLightFromMatrix(i, j).getLightState() == 0)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(SWS));
                else if(i%2 == 1 && j%2 == 1 && lc.getLightFromMatrix(i, j).getLightState() == 1)
                    lightImageMatrix[i][j].setIcon(new ImageIcon(SWW));
                else
                    System.err.println("Error initializing light images.");
            }
        }
    }
    
    private void lightsAnimator() //main function of the lights thread, this handles the light change interval, and the changing of the lights
    {
        lc = new LightsControl(); //this initializes all of the light values
        
        try
        {
            while(true)
            {
                
                lightThread.sleep(lightSpeed.getValue()); //getting value from slider to determine the sleep time
                lc.changeLights(); //make every light change
                
                refreshLights();
            }
        }
        catch(InterruptedException ie)
        {
            //If this thread was intrrupted by nother thread
        }
    }
    
    private void initializeCars() //move the car images to their corresponding locations, and have them face the correct direction, this is called after the car values have been determined
    {
        int numCars = car.getNumcars();
        int i, dir;
        
        for(i=0; i<numCars; i++)
        {
            carImageBuffer = (JLabel)carImageArray.get(i);
            carImageBuffer.setLocation(14*car.getCarFromArray(i).getOMJ(), 14*car.getCarFromArray(i).getOMI()); //each OMI and OMJ value represents 14 pixels in length (42/3)
            car.getCarFromArray(i).setPixelLoc(14*car.getCarFromArray(i).getOMJ(), 14*car.getCarFromArray(i).getOMI()); //OMI = occupancy matrix I
            dir = car.getCarFromArray(i).getDirection();
            
            if(dir == 0)
                carImageBuffer.setIcon(new ImageIcon(CARN));
            else if(dir == 1)
                carImageBuffer.setIcon(new ImageIcon(CARE));
            else if(dir == 2)
                carImageBuffer.setIcon(new ImageIcon(CARS));
            else if(dir == 3)
                carImageBuffer.setIcon(new ImageIcon(CARW));
        }
        
    }
    
    private void moveCars() //move each car one pixel in the proper direction, if it is movable, and update the car's internal variables as well to reflect the new position
    {
        int numCars = car.getNumcars();
        int i, carX, carY, dir;
        
        
        for(i = 0; i < numCars; i++)
        {
            if(car.getCarFromArray(i).getDrive() && !car.getCarFromArray(i).getStoppedAtLight() && !car.getCarFromArray(i).getNextStreetFull())
            {
                carImageBuffer = (JLabel)carImageArray.get(i);
                carX = car.getCarFromArray(i).getPixelX(); //sort of self explanatory
                carY = car.getCarFromArray(i).getPixelY();
                dir = car.getCarFromArray(i).getDirection(); //common theme throughout 0 = north, 1 = east, 2 = south, 3 = west
                if(dir == 0)
                {
                    carImageBuffer.setLocation(carX, carY-1);
                    car.getCarFromArray(i).setPixelLoc(carX, carY-1);
                }
                else if(dir == 1)
                {
                    carImageBuffer.setLocation(carX+1, carY);
                    car.getCarFromArray(i).setPixelLoc(carX+1, carY);
                }
                else if(dir == 2)
                {
                    carImageBuffer.setLocation(carX, carY+1);
                    car.getCarFromArray(i).setPixelLoc(carX, carY+1);
                }
                else if(dir == 3)
                {
                    carImageBuffer.setLocation(carX-1, carY);
                    car.getCarFromArray(i).setPixelLoc(carX-1, carY);
                }
            }
        }
    }
    
    private void checkCarsAgainstLights() //checks all cars against the lights to see if the light is red or green, and sets the car to stop or go
    {
        int numCars = car.getNumcars();
        int i, dir, x, y, lightI, lightJ;
        for(i = 0; i < numCars; i++)
        {
            
            dir = car.getCarFromArray(i).getDirection();
            x = car.getCarFromArray(i).getPixelX();
            y = car.getCarFromArray(i).getPixelY();
            
            
            if(x < 0 || x > 504 || y < 0 || y > 504)
            {
                //do nothing
            }
            else if(dir == 0)//north
            {
                
                
                if(y%56 == 14) //if at an intersection
                {
                    lightI = y/56; //street width + block width = 56
                    lightJ = x/56;
                    
                    if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 0)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(false);
                    }
                    else if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 1)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(true);
                    }
                }
            }
            else if(dir == 1)//east
            {
                if(x%56 == 42) //if at an intersection
                {
                    lightI = y/56; //street width + block width = 56
                    lightJ = (x/56)+1;
                    
                    if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 1)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(false);
                    }
                    else if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 0)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(true);
                    }
                }
            }
            else if(dir == 2) //south
            {
                if(y%56 == 42) //if at an intersection
                {
                    lightI = (y/56)+1; //street width + block width = 56
                    lightJ = x/56;
                    
                    if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 0)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(false);
                    }
                    else if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 1)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(true);
                    }
                }
            }
            else if(dir == 3)//west
            {
                if(x%56 == 14) //if at an intersection
                {
                    lightI = y/56; //street width + block width = 56
                    lightJ = x/56;
                    
                    if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 1)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(false);
                    }
                    else if(lc.getLightFromMatrix(lightI, lightJ).getLightState() == 0)
                    {
                        car.getCarFromArray(i).setStoppedAtLight(true);
                    }
                }
            }
        }
    }
    
    private void checkCarsAgainstOM() //one of the major functions, keeps the occupancy matrix updated, and allows the cars to "see" other cars via this matrix. This is a map, if you will, of all of the cars' locations.
    {
        
        //If a car is at the exact pixel location of a slot, then check if the next slot is open, if it is, then release the current slot and claim the new slot and drive
        int i, numCars, dir, x, y, omI, omJ;
        numCars = car.getNumcars();
        
        for(i = 0; i < numCars; i++)
        {
            dir = car.getCarFromArray(i).getDirection();
            x = car.getCarFromArray(i).getPixelX();
            y = car.getCarFromArray(i).getPixelY();
            omI = car.getCarFromArray(i).getOMI();
            omJ = car.getCarFromArray(i).getOMJ();
            
            if(x%14 == 0 && y%14 == 0 && !car.getCarFromArray(i).getStoppedAtLight() && !car.getCarFromArray(i).getNextStreetFull()) //if in the slot, look ahead to next slot and claim it if vacant
            {
                if(dir == 0 && y > 0) //north
                {
                    //System.out.println("Dir: "+dir+"\t"+omI+"\t"+omJ+"\t"+x+"\t"+y);
                    //System.out.println("Car: "+i+" OMI: "+omI+" OMJ: "+omJ);
                    if(car.getOMValue(omI-1, omJ) == 0) //look ahead to see if the next OM slot is available, if it is, claim it and set car's drive flag to true (via go())
                    {
                        car.setOMValue(omI, omJ, 0);
                        car.getCarFromArray(i).setOMLocation(omI-1, omJ);
                        car.setOMValue(omI-1, omJ, 1);
                        car.getCarFromArray(i).go();
                    }
                    else if(car.getOMValue(omI-1, omJ) == 1)
                        car.getCarFromArray(i).stop();
                }
                else if(dir == 1 && x < 504) //east
                {
                    //System.out.println("Dir: "+dir+"\t"+omI+"\t"+omJ+"\t"+x+"\t"+y);
                    //System.out.println("Car: "+i+" OMI: "+omI+" OMJ: "+omJ);
                    if(car.getOMValue(omI, omJ+1) == 0)
                    {
                        car.setOMValue(omI, omJ, 0);
                        car.getCarFromArray(i).setOMLocation(omI, omJ+1);
                        car.setOMValue(omI, omJ+1, 1);
                        car.getCarFromArray(i).go();
                    }
                    else if(car.getOMValue(omI, omJ+1) == 1)
                        car.getCarFromArray(i).stop();
                }
                else if(dir == 2 && y < 504) //south
                {
                    //System.out.println("Dir: "+dir+"\t"+omI+"\t"+omJ+"\t"+x+"\t"+y);
                    //System.out.println("Car: "+i+" OMI: "+omI+" OMJ: "+omJ);
                    if(car.getOMValue(omI+1, omJ) == 0)
                    {
                        car.setOMValue(omI, omJ, 0);
                        car.getCarFromArray(i).setOMLocation(omI+1, omJ);
                        car.setOMValue(omI+1, omJ, 1);
                        car.getCarFromArray(i).go();
                    }
                    else if(car.getOMValue(omI+1, omJ) == 1)
                        car.getCarFromArray(i).stop();
                }
                else if(dir == 3 && x > 0) //west
                {
                    //System.out.println("Dir: "+dir+"\t"+omI+"\t"+omJ+"\t"+x+"\t"+y);
                    //System.out.println("Car: "+i+" OMI: "+omI+" OMJ: "+omJ);
                    if(car.getOMValue(omI, omJ-1) == 0)
                    {
                        car.setOMValue(omI, omJ, 0);
                        car.getCarFromArray(i).setOMLocation(omI, omJ-1);
                        car.setOMValue(omI, omJ-1, 1);
                        car.getCarFromArray(i).go();
                    }
                    else if(car.getOMValue(omI, omJ-1) == 1)
                        car.getCarFromArray(i).stop();
                }
            }
        }
    }
    
    private void checkCarsAgainstPath()
    {
        /*
         * If a car is about to claim an intersection:
         * 1. check the path to see if we are turning
         * 2. check the OM to see if the slot after the intersection is open (so cars aren't sitting in intersections)
         * 3. if the slot after the intersection is NOT open, then DO NOT claim the intersection, wait until next move to check again
         * 4. if the slot after the intersection IS open, then claim the intersection, and move
         * 5. if turning, then change the car image icon to the correct direction once the car is fully in the intersection.
         * 
         * 
         * The nextStreetFull variable is the main focus for this function, as well as setting the turning variable and direction
         * 
         * If a car is turning, this will set the car's internal "isTurning" variable to true, and sets the direction.
         * I decided to always set this to true and if the car is going in the same direction, just keep using that direction
         */
        
        int numCars = car.getNumcars();
        int i, dir, x, y, omi, omj, next;
        for(i = 0; i < numCars; i++)
        {
            
            dir = car.getCarFromArray(i).getDirection();
            x = car.getCarFromArray(i).getPixelX();
            y = car.getCarFromArray(i).getPixelY();
            omi = car.getCarFromArray(i).getOMI();
            omj = car.getCarFromArray(i).getOMJ();
            
            if(x < 0 || x > 504 || y < 0 || y > 504 || car.getCarFromArray(i).getStoppedAtLight() || !car.getCarFromArray(i).getDrive())
            {
                //System.out.println("car is stopped or not drivable");//do nothing
            }
            else if(dir == 0)//north
            {
                if(y%56 == 14) //if at an intersection
                {
                    
                    //System.out.println("Travelling NORTH and at an intersection. isEmpty: " + car.getCarFromArray(i).isPathEmpty() + ", Current Dest: " + car.getCarFromArray(i).getCurrentDest()
                            //+ ", Current omi: " + omi + ", Current omj: " + omj);
                    //if we have arrived to current destination, then get next
                    if(!car.getCarFromArray(i).isPathEmpty() && 
                        omi-1 == 4 * (car.getCarFromArray(i).getCurrentDest() / 10)) //if this next intersection is our current destination
                    {
                        next = car.getCarFromArray(i).getNextIntersection();
                        
                        //System.out.println("Car: " + i + ", Dir: " + dir + ", OMI: " + omi + ", OMJ: " + omj + " Next: " + next);
                        if(4*(next%10) < omj) //west
                        {
                            if(car.getOMValue(omi-1, omj-1) == 0) //up and left in OM
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(3); //setting turn flag to true, and direction to west
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next%10) == omj) //north
                        {
                            if(car.getOMValue(omi-2, omj) == 0) //up 2 in OM
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(0);//not turning
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next%10) > omj) //east
                        {
                            if(car.getOMValue(omi-1, omj+1) == 0) //up and right in OM
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(1); //setting turn flag to true, and direction to east
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else
                            System.err.println("Error in NORTH statement of checkCarsAgainstPath");
                    }
                    else if(car.getCarFromArray(i).isPathEmpty() &&
                        omi-1 == 4 * (car.getCarFromArray(i).getCurrentDest() / 10)) //if this next intersection is our current destination AND our path is empty, despawn
                    {
                        car.getCarFromArray(i).setCarToDespawn();
                    }
                }
            }
            else if(dir == 1)//east
            {
                if(x%56 == 42) //if at an intersection
                {
                    
                    //System.out.println("Travelling EAST and at an intersection. isEmpty: " + car.getCarFromArray(i).isPathEmpty() + ", Current Dest: " + car.getCarFromArray(i).getCurrentDest()
                            //+ ", Current omi: " + omi + ", Current omj: " + omj);
                    //if we have arrived to current destination, then get next
                    if(!car.getCarFromArray(i).isPathEmpty() &&
                        omj+1 == 4 * (car.getCarFromArray(i).getCurrentDest() % 10)) //if this next intersection is our current destination
                    {
                        next = car.getCarFromArray(i).getNextIntersection();
                        
                        //System.out.println("Car: " + i + ", Dir: " + dir + ", OMI: " + omi + ", OMJ: " + omj + " Next: " + next);
                        if(4*(next/10) < omi) //north
                        {
                            if(car.getOMValue(omi-1, omj+1) == 0)
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(0); //setting turn flag to true
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next/10) == omi) //east
                        {
                            if(car.getOMValue(omi, omj+2) == 0)
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(1);//not turning
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next/10) > omi) //south
                        {
                            if(car.getOMValue(omi+1, omj+1) == 0) 
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(2); //setting turn flag to true, and direction to south
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else
                            System.err.println("Error in EAST statement of checkCarsAgainstPath");
                    }
                    else if(car.getCarFromArray(i).isPathEmpty() &&
                        omj+1 == 4 * (car.getCarFromArray(i).getCurrentDest() % 10)) //if this next intersection is our current destination AND our path is empty, despawn
                    {
                        car.getCarFromArray(i).setCarToDespawn();
                    }
                }
            }
            else if(dir == 2) //south
            {
                if(y%56 == 42) //if at an intersection
                {
                    
                    //System.out.println("Travelling SOUTH and at an intersection. isEmpty: " + car.getCarFromArray(i).isPathEmpty() + ", Current Dest: " + car.getCarFromArray(i).getCurrentDest()
                            //+ ", Current omi: " + omi + ", Current omj: " + omj);
                    //if we have arrived to current destination, then get next
                    if(!car.getCarFromArray(i).isPathEmpty() &&
                        omi+1 == 4 * (car.getCarFromArray(i).getCurrentDest() / 10)) //if this next intersection is our current destination
                    {
                        next = car.getCarFromArray(i).getNextIntersection();
                        
                        //System.out.println("Car: " + i + ", Dir: " + dir + ", OMI: " + omi + ", OMJ: " + omj + " Next: " + next);
                        if(4*(next%10) > omj) //east
                        {
                            if(car.getOMValue(omi+1, omj+1) == 0)
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(1); //setting turn flag to true
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next%10) == omj) //south
                        {
                            if(car.getOMValue(omi+2, omj) == 0)
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(2);//not turning
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next%10) < omj) //west
                        {
                            if(car.getOMValue(omi+1, omj-1) == 0) 
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(3); //setting turn flag to true, and direction to west
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else
                            System.err.println("Error in SOUTH statement of checkCarsAgainstPath");
                    }
                    else if(car.getCarFromArray(i).isPathEmpty()) //if this next intersection is our current destination AND our path is empty, despawn
                    {
                        car.getCarFromArray(i).setCarToDespawn();
                    }
                }
            }
            else if(dir == 3)//west
            {
                if(x%56 == 14) //if at an intersection
                {
                    
                    //System.out.println("Travelling WEST and at an intersection. isEmpty: " + car.getCarFromArray(i).isPathEmpty() + ", Current Dest: " + car.getCarFromArray(i).getCurrentDest()
                            //+ ", Current omi: " + omi + ", Current omj: " + omj);
                   //if we have arrived to current destination, then get next
                    if(!car.getCarFromArray(i).isPathEmpty() &&
                        omj-1 == 4 * (car.getCarFromArray(i).getCurrentDest() % 10)) //if this next intersection is our current destination
                    {
                        next = car.getCarFromArray(i).getNextIntersection();
                        
                        //System.out.println("Car: " + i + ", Dir: " + dir + ", OMI: " + omi + ", OMJ: " + omj + " Next: " + next);
                        if(4*(next/10) < omi) //north
                        {
                            if(car.getOMValue(omi-1, omj-1) == 0)
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(0); //setting turn flag to true
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next/10) == omi) //west
                        {
                            if(car.getOMValue(omi, omj-2) == 0)
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(3);//not turning
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else if(4*(next/10) > omi) //south
                        {
                            if(car.getOMValue(omi+1, omj-1) == 0) 
                            {
                                car.getCarFromArray(i).setNextStreetFull(false);//slot is open, clear to go
                                car.getCarFromArray(i).turn(2); //setting turn flag to true, and direction to south
                                car.getCarFromArray(i).removeIntersectionFromPath();
                            }
                            else
                                car.getCarFromArray(i).setNextStreetFull(true);
                        }
                        else
                            System.err.println("Error in WEST statement of checkCarsAgainstPath");
                    }
                    else if(car.getCarFromArray(i).isPathEmpty() &&
                        omj-1 == 4 * (car.getCarFromArray(i).getCurrentDest() % 10)) //if this next intersection is our current destination AND our path is empty, despawn
                    {
                        car.getCarFromArray(i).setCarToDespawn();
                    }
                }
            }
        }
        
    }
    
    private void turnCar() //checks to see which direction the cars need to go when entering an intersection, and sets the cars' direction to their queued direction, and changes their images to reflect the change in direction 
    {
        int i, numcars, turningDir, x, y;
        numcars = car.getNumcars();
        
        
        for(i = 0; i < numcars; i++)
        {
            x = car.getCarFromArray(i).getPixelX();
            y = car.getCarFromArray(i).getPixelY();
            carImageBuffer = (JLabel)carImageArray.get(i);
            
            if(x%56 == 0 && y%56 == 0 && car.getCarFromArray(i).isTurning()) //if car is in an intersection AND turning
            {
                turningDir = car.getCarFromArray(i).getTurningDirection();
                //System.out.println("Turning direction is now: " + turningDir + ", while at intersection: " + y/56 + "" + x/56);
                //System.out.println(y/56 + "" + x/56 + " ");
                car.getCarFromArray(i).setDirection(turningDir);
                
                if(car.getCarFromArray(i).getIsUserCar())
                {
                    if(turningDir == 0)
                        carImageBuffer.setIcon(new ImageIcon(USERCARN));
                    else if(turningDir == 1)
                        carImageBuffer.setIcon(new ImageIcon(USERCARE));
                    else if(turningDir == 2)
                        carImageBuffer.setIcon(new ImageIcon(USERCARS));
                    else if(turningDir == 3)
                        carImageBuffer.setIcon(new ImageIcon(USERCARW));
                }
                else
                {
                    if(turningDir == 0)
                        carImageBuffer.setIcon(new ImageIcon(CARN));
                    else if(turningDir == 1)
                        carImageBuffer.setIcon(new ImageIcon(CARE));
                    else if(turningDir == 2)
                        carImageBuffer.setIcon(new ImageIcon(CARS));
                    else if(turningDir == 3)
                        carImageBuffer.setIcon(new ImageIcon(CARW));
                }
                
                //System.out.println("Car: "+i+" just turned to direction: "+turningDir+" with OMI: "+car.getCarFromArray(i).getOMI()+" and OMJ: "+car.getCarFromArray(i).getOMJ());
                
                car.getCarFromArray(i).finishedTurn();
            }
            
        }
    }
    
    private void checkCarsForDespawn() 
    {
        /*
         * this will immediately overwrite the car's current values with new ones 
         * that are randomly generated, essentially "despawning" the old car and 
         * "spawning" the new one at once. It also references the goal for the number 
         * of cars, so if goal < current number of cars, a car will fully despawn instead
         * of spawning a new one.
         */
        int i, numcars, numcarsGoal, dir;
        numcars = car.getNumcars();
        
        for(i = 0; i < numcars; i++)
        {
            carImageBuffer = (JLabel)carImageArray.get(i);
            
            numcarsGoal = car.getNumCarsGoal();
            
            if(car.getCarFromArray(i).getDespawnFlag())
            {
                if(numcarsGoal < numcars)
                {
                    if(car.getCarFromArray(i).getIsUserCar()) //if this car is a user generated car, set the button to be clickable again
                        userCarButton.setEnabled(true);
                    
                    carImageBuffer = (JLabel)carImageArray.get(i);
                    carImageBuffer.setIcon(null);
                    car.setOMForDespawnedCar(i);
                    car.removeCarFromList(i);
                    carImageArray.remove(i);
                    car.reduceNumCarsByOne();
                    
                    //System.out.println("Car: " + i + " has been despawned.");
                    //car.printOM();
                    //System.out.println();
                }
                else if(numcarsGoal >= numcars)
                {
                    if(car.getCarFromArray(i).getIsUserCar()) //if this car is a user generated car, set the button to be clickable again
                        userCarButton.setEnabled(true);
                    
                    car.spawnNewCar(i); //generates new car at an empty intersection
                    
                    carImageBuffer.setLocation(14*car.getCarFromArray(i).getOMJ(), 14*car.getCarFromArray(i).getOMI());
                    car.getCarFromArray(i).setPixelLoc(14*car.getCarFromArray(i).getOMJ(), 14*car.getCarFromArray(i).getOMI());
                    dir = car.getCarFromArray(i).getDirection();
                    
                    if(dir == 0)
                        carImageBuffer.setIcon(new ImageIcon(CARN));
                    else if(dir == 1)
                        carImageBuffer.setIcon(new ImageIcon(CARE));
                    else if(dir == 2)
                        carImageBuffer.setIcon(new ImageIcon(CARS));
                    else if(dir == 3)
                        carImageBuffer.setIcon(new ImageIcon(CARW));
                }
            }
            numcars = car.getNumcars();
        }
    }
    
    private void checkForNewCar() //creates a new car when needed, i.e. when our number of cars goal > number of cars, which is represented by the new car flag
    {
        int numCars, dir;
        
        if(car.getNewCarFlag())
        {
            car.resetNewCarFlag();
            
            carImageBuffer = new JLabel();
            //carImageBuffer.setIcon(new ImageIcon("carS.gif")); //these images are being spawned "out of frame" and simply moved to the correct place when the cars are spawned
            carImageBuffer.setBounds(-20, -20, 14, 14); //-20, -20 gets it just out of frame
            carLayer.add(carImageBuffer, new Integer(0)); //adding cars to the car layer at level 0, but the car layer itself will be level 2 in the bgLayer
            carImageArray.add(carImageBuffer);
            
            numCars = car.getNumcars();
            carImageBuffer = (JLabel)carImageArray.get(numCars-1);
            carImageBuffer.setLocation(14*car.getCarFromArray(numCars-1).getOMJ(), 14*car.getCarFromArray(numCars-1).getOMI());
            car.getCarFromArray(numCars-1).setPixelLoc(14*car.getCarFromArray(numCars-1).getOMJ(), 14*car.getCarFromArray(numCars-1).getOMI());
            dir = car.getCarFromArray(numCars-1).getDirection();
            
            if(dir == 0)
                    carImageBuffer.setIcon(new ImageIcon(CARN));
                else if(dir == 1)
                    carImageBuffer.setIcon(new ImageIcon(CARE));
                else if(dir == 2)
                    carImageBuffer.setIcon(new ImageIcon(CARS));
                else if(dir == 3)
                    carImageBuffer.setIcon(new ImageIcon(CARW));
        }
    }
    
    private void carAnimator() //main function of the car thread, handles everything to do with the cars
    {
        //car = new CarsControl();
        initializeCars();
        //int i;
        try
        {
            while(true)
            { //the ordering of these functions is very important, they must stay in this order
                
                Thread.sleep(carSpeed.getValue()); //affects car speed
                
                car.setNumCarsGoal(carNumber.getValue());
                
                checkCarsForDespawn(); //all the time
                
                car.checkNumCarsGoal();
                
                checkForNewCar();
                
                checkCarsAgainstLights(); //if car is at an intersection
                
                checkCarsAgainstPath(); //if car is at an intersection
                
                turnCar(); //must be called after path check, while IN an intersection
                
                
                
                checkCarsAgainstOM(); //called at every slot must be done after turnCar b/c it needs the updated direction to check the OM correctly
                
                
                //for(i=0; i<14; i++)
                //{
                    moveCars();
                //}
                
                
                //car.printOM();
            }
        }
        catch(InterruptedException e)
        {
        
        }
    
    
    }
    
    
    
    
}
